package com.test.redis_demo.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void test() {
        String lock = "lock";
        Object t = redisTemplate.execute((RedisCallback) connection -> {
            long lockTimeout = 300;
            long expireAt = System.currentTimeMillis() + 300 + 1;
            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
            if (acquire) {
                return true;
            } else {

                byte[] value = connection.get(lock.getBytes());

                if (Objects.nonNull(value) && value.length > 0) {

                    long expireTime = Long.parseLong(new String(value));
                    // 如果锁已经过期
                    if (expireTime < System.currentTimeMillis()) {
                        // 重新加锁，防止死锁
                        byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + 300 + 1).getBytes());
                        return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });
    }

    /**
     * 1、先看它的add方法，Boolean add(K key, V value, double score);
     * 这个有序队列的结构就是Key，Value，Score
     * key就是这个有序队列的key，
     * value表示一个你需要排序附带的值，比如你可以放一个用户的ID或者其他的。
     * Score表示一个分数，所有的排序都是基于这个score。可以正序排列，也可以倒叙排列。
     *
     * 2、Set< V > range(K key, long start, long end);  正序
     * Set reverseRange(K key, long start, long end);倒叙
     * 获取队列的方法，start表示起始位置的index，从0开始。index表示end的位置，-1表示获取全部
     * opsForZSet.range("key",0,-1)，表示获取key队列的所有元素。
     * 对应的方法还有几个
     * Set< TypedTuple< V >> rangeWithScores(K key, long start, long end);
     * Set< TypedTuple< V >> reverseRangeWithScores(K key, long start, long end);
     * 这两个方法跟上面的方法差不多，只是返回的时候回带上score，有时候业务需要输出这个score，比如排行榜的分数，你就可以用2个这个。
     * 3、
     * Set< V > rangeByScore(K key, double min, double max);
     * Set< V > reverseRangeByScore(K key, double min, double max);
     * 这俩方法表示在某个分数区间内的集合。min表示最小的分数，max表示最大的分数，集合返回在min和max之间的集合有哪些。
     * 业务场景：比如某个分数区间内的用户数量统计，或者对这些用户给予一些奖励。具体就不赘述了。
     * 类似的方法如下
     * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max);
     * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max, long offset, long count);
     * Set< TypedTuple< V >> reverseRangeByScoreWithScores(K key, double min, double max);
     * Set< V > reverseRangeByScore(K key, double min, double max, long offset, long count);
     * Set< TypedTuple< V >> reverseRangeByScoreWithScores(K key, double min, double max, long offset, long count);
     * 这几个方法比较类似，要么是倒叙，要么是对返回对象进行了限制输出，加上了offset，count来限制输出的对象，就是类似于数据库的limit，offset。
     * 4、
     * Long size(K key);
     * Long zCard(K key);
     * 这两方法表示的都是这个队列的长度，不知道为啥要写俩个，size()底层就是调用的zCard()
     * 5、
     * Long unionAndStore(K key, K otherKey, K destKey);
     * Long unionAndStore(K key, Collection< K > otherKeys, K destKey);
     * 这两个方法挺有意思的，意思不仅仅是去重，而且会把重复的数据score进行相加，返回值是这个集合的长度
     * 先解释第一个，key，otherKey这两个key的有序队列进行相加，如果两个队列中存在相同的value，就将value的score相加，最终将最后的组合结果放到destKey中。
     * 第二个方法则是多了一个集合参数，多个集合可以用同一个方法，节省代码，举个简单的例子：
     * Long unionAndStore2 = opsForZSet.unionAndStore("A", Arrays.asList("B","C"), "D");
     * 6、
     * Long intersectAndStore(K key, K otherKey, K destKey);
     * Long intersectAndStore(K key, Collection< K > otherKeys, K destKey);
     * 这俩方法刚好和上面2个方法相反，这个是交集。把交集的结果放到destKey中
     * 7、
     * Cursor< TypedTuple< V >> scan(K key, ScanOptions options);
     * 这方法自己用的时候，发现跟interator基本上一样，就是用来遍历这个key的集合里面的所有元素的。
     * @return
     */
    @RequestMapping("userScore")
    @ResponseBody
    public JSONObject getUserScore(){
        String key = "user:score";
        JSONArray arry = new JSONArray();
        JSONObject json1 = new JSONObject();
        json1.put("id",1);
        json1.put("name",123);
        json1.put("score",3);
        JSONObject json2 = new JSONObject();
        json2.put("id",2);
        json2.put("name",234);
        json2.put("score",24);
        JSONObject json3 = new JSONObject();
        json3.put("id",3);
        json3.put("name",345);
        json3.put("score",34);
        JSONObject json4 = new JSONObject();
        json4.put("id",4);
        json4.put("name",456);
        json4.put("score",26);
        JSONObject json5 = new JSONObject();
        json5.put("id",5);
        json5.put("name",567);
        json5.put("score",7);
        arry.add(json1);
        arry.add(json2);
        arry.add(json3);
        arry.add(json4);
        arry.add(json5);
        for (int i = 0; i < arry.size(); i++) {
            JSONObject d = arry.getJSONObject(i);
            redisTemplate.opsForZSet().add(key,d,d.getDouble("score"));
        }
        JSONObject json = new JSONObject();
        json.put("opsForZSet().count",redisTemplate.opsForZSet().count(key,0,23));
        json.put("opsForZSet().range",redisTemplate.opsForZSet().range(key,0,3));
        json.put("opsForZSet().rangeByLex",redisTemplate.opsForZSet().rangeByLex(key,RedisZSetCommands.Range.range()));
        json.put("opsForZSet().reverseRange",redisTemplate.opsForZSet().reverseRange(key,0,3));
        return json;
    }

    public static void main(String[] args) {
        Integer[] aa = {12,34,456,3,123,312,5,45645,456,234};
        List<Integer> kk = Arrays.asList(aa);
        kk.stream().map((k)->k*2).forEach(System.out::println);
        kk = kk.stream().map((k)->k*2).collect(Collectors.toList());
        System.out.println("---------");
        kk.forEach(n-> System.out.println(n));
        /*String[] a = {"qwe","ka","2131","a5sd","45f4"};
        List aa = Arrays.asList(a);
        filter(aa,(t)->t.equals("ka"));*/
        /*aa.forEach(n-> System.out.println(n));
        System.out.println("----------");
        aa.forEach(System.out::println);*/
        /*for (int i = 0; i < 5; i++) {
            new Thread(() -> System.out.println("测试一下")).start();
        }*/
    }
    @RequestMapping("rankas")
    public void rankas() {
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add("score", "one", 1);
        zSetOperations.add("score", "four", 4);
        zSetOperations.add("score", "three", 110);
        zSetOperations.add("score", "five", 5);
        zSetOperations.add("score", "six", 6);
        //从子集中找到Smin<=score<=Smax的元素集合
        //value在此区间中的。
        Set set = zSetOperations.rangeByScore("score", 100, 200);
        System.out.println("打印v1的值在100-120区间的" + set.size());
        //索引start<=index<=end的元素子集，返回泛型接口（包括score和value），正序
        //返回score和value，set中的前两个
        Set set1 = zSetOperations.rangeWithScores("score", 0, 1);
        //键为K的集合，索引start<=index<=end的元素子集，正序
        //返回排序后的value中的前两个
        Set set2 = zSetOperations.range("score", 0, 1);
        //键为K的集合，索引start<=index<=end的元素子集，倒序
        //返回排序后的最后两个
        Set set3 = zSetOperations.reverseRange("score", 0, 1);
    }
    public static void filter(List<String> aa, Predicate predicate){
        for (String tt : aa){
            if (predicate.test(tt)){
                System.out.println(tt);
            }
        }
        aa.stream().filter((tt) ->(predicate.test(tt))).forEach(t-> System.out.println(t));
    }
}
